package service

import (
	sqlc "bgs/db/sqlc"
	"bgs/service/model"
	"bgs/util"
	"bgs/wxp/mina"
	"context"
	"encoding/json"
	"fmt"
)

// BackupSubMsgWhenHandleFail go func 执行
func (s *ActivityServiceImp) AsyncCallHandleFail(ctx context.Context, err *error) {
	var errMsg string
	if r := recover(); r != nil {
		errMsg = fmt.Sprintf("AsyncCallHandleFail recover panic:%s", r)
	} else if err != nil && (*err) != nil {
		errMsg = fmt.Sprintf("AsyncCallHandleFail normal err:%s", (*err).Error())
	}
	if errMsg != "" {
		s.slog.Errorf("AsyncCallHandleFail err:%v", errMsg)
	}
}

type AsyncSendUserNotificationByActivityIDParam struct {
	ActivityID int64
	Type       sqlc.EmActivityNotificationType
	Offset     int32
	Limit      int32
}

type UserIDMapUserOpenID map[int64]string

func (s *ActivityServiceImp) AsyncSendUserNotificationByActivityID(ctxRaw context.Context, p AsyncSendUserNotificationByActivityIDParam) {
	slog := util.NewContextLogger(ctxRaw, s.slog)
	var err error

	activityID := p.ActivityID
	notificationType := string(p.Type)
	cActivity := s.cacher.FetchActivity(activityID)
	if cActivity == nil {
		slog.Errorf("not found activity of activityID:%d", activityID)
		return
	}
	slog.Infof("AsyncSendUserNotificationByActivityID 读取活动缓存 ok")

	brandID := cActivity.BrandID
	activityType := cActivity.Type
	typeInfo := cActivity.TypeInfoPtr
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		slog.Errorf("not found brand of brandID:%d", brandID)
		return
	}
	slog.Infof("AsyncSendUserNotificationByActivityID 读取品牌缓存 ok")

	ctx := context.Background()
	defer s.AsyncCallHandleFail(ctx, &err)
	err = s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		notifications, err := q.ListActivityUserNotificationByActivityIDToLock(ctx, sqlc.ListActivityUserNotificationByActivityIDToLockParams{
			ActivityID: p.ActivityID,
			Type:       notificationType,
			Offset:     p.Offset,
			Limit:      p.Limit,
		})
		if err != nil {
			return
		}
		slog.Infof("AsyncSendUserNotificationByActivityID 读取活动需要发送提醒通知的用户 ok")

		var userIDs []int64
		userIDMapUserOpenID := make(UserIDMapUserOpenID)
		for _, notinotification := range notifications {
			if notinotification.TokenCount <= 0 {
				slog.Infof("发现token_count<=0的user_id:%d", notinotification.TokenCount)
				continue
			}
			userIDs = append(userIDs, notinotification.UserID)
			userIDMapUserOpenID[notinotification.UserID] = notinotification.UserOpenID
		}

		rows, err := q.ListActivityUserProcedureUserIDAndExtInfosByActivityIDAndUserIDs(ctx, sqlc.ListActivityUserProcedureUserIDAndExtInfosByActivityIDAndUserIDsParams{
			ActivityID: activityID,
			UserIDs:    userIDs,
		})
		if err != nil {
			return
		}
		slog.Infof("AsyncSendUserNotificationByActivityID 读取活动需要发送提醒通知用户的活动过程数据 ok")

		var UserCheckIn1s []*UserCheckIn1
		for _, row := range rows {
			procedureExtInfoPtr := &model.ActivityUserProcedureExtInfo{}
			json.Unmarshal(row.ExtInfo.RawMessage, procedureExtInfoPtr)
			switch sqlc.EmActivityType(activityType) {
			case sqlc.EmActivityTypeCheckIn1:
				switch p.Type {
				case sqlc.EmActivityNotificationTypeRemind:
					UserCheckIn1s = append(UserCheckIn1s, &UserCheckIn1{
						UserOpenID:             userIDMapUserOpenID[row.UserID],
						ContinuousCheckInCount: procedureExtInfoPtr.CheckIn1.ContinuousCheckInCount,
						ContinuousMaxCount:     typeInfo.CheckIn1Ptr.MaxContinuous,
					})
				}
			}
		}

		var notifyParams []*mina.SendNotifyParam
		if len(UserCheckIn1s) > 0 {
			notifyParams, err = s.GenRemindCheckIn1NotifyMessage(ctx, q, GenRemindCheckIn1NotifyMessageParam{
				ActivityID:    activityID,
				ActivityName:  cActivity.Name,
				BrandID:       brandID,
				BrandName:     cBrand.Name,
				UserCheckIn1s: UserCheckIn1s,
			})
			slog.Infof("AsyncSendUserNotificationByActivityID 签到通知参数准备完毕 ok")
		}

		chNotify := s.cm.Notifier.GetWorker()
		for _, notifyParam := range notifyParams {
			chNotify <- notifyParam
			slog.Infof("AsyncSendUserNotificationByActivityID 通知进入发送队列 ok")
			// 将UpdateActivityUserProcedureSended 从用主键改为用activityID+type+userID做的唯一约束来更新
			_, err = q.BatchMarkActivityUserNotificationsSended(ctx, sqlc.BatchMarkActivityUserNotificationsSendedParams{
				ActivityID: activityID,
				Type:       notificationType,
				UserIDs:    userIDs,
			})
			if err != nil {
				return
			}
		}

		return
	})
	if err != nil {
		slog.Errorf("AsyncSendUserNotificationByActivityID err=>%s", err.Error())
		return
	}

	slog.Infof("异步发送活动用户通知 ok")
}

type UpsertActivityUserNotificationParam struct {
	ActivityPtr *sqlc.Activity
	UserID      int64
	UserOpenID  string
	Type        sqlc.EmActivityNotificationType
}

// UpsertActivityUserNotification 新增/更新活动用户通知
func (s *ActivityServiceImp) UpsertActivityUserNotification(ctx context.Context, q *sqlc.Queries, p UpsertActivityUserNotificationParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	activityID := p.ActivityPtr.ID
	brandID := p.ActivityPtr.BrandID
	userID := p.UserID
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}

	var activityUserNotificationExtInfoPtr *model.ActivityUserNotificationExtInfo

	if p.ActivityPtr.Type == string(sqlc.EmActivityTypeCheckIn1) {
		activityUserProcedure, _, sqlErr := s.EnsureActivityUserProcedure(ctx, q, false, activityID, userID)
		if sqlErr != nil {
			err = sqlErr
			return
		}
		procedureExtInfoPtr := &model.ActivityUserProcedureExtInfo{}
		json.Unmarshal(activityUserProcedure.ExtInfo.RawMessage, procedureExtInfoPtr)

		typeInfoPtr := &model.ActivityTypeInfo{}
		json.Unmarshal(p.ActivityPtr.TypeInfo.RawMessage, typeInfoPtr)

		activityUserNotificationExtInfoPtr = &model.ActivityUserNotificationExtInfo{
			CheckIn1RemindPtr: &model.CheckIn1RemindNotification{
				BrandName:              cBrand.Name,
				ActivityName:           p.ActivityPtr.Name,
				ContinuousCheckInCount: procedureExtInfoPtr.CheckIn1.ContinuousCheckInCount,
				ContinuousMaxCount:     typeInfoPtr.CheckIn1Ptr.MaxContinuous,
			},
		}
	}

	arg := sqlc.UpsertActivityUserNotificationParams{
		BrandID:          brandID,
		ActivityID:       activityID,
		ActivityType:     p.ActivityPtr.Type,
		RootActivityID:   p.ActivityPtr.RootID,
		ParentActivityID: p.ActivityPtr.ParentID,
		Type:             string(p.Type),
		UserID:           userID,
		UserOpenID:       p.UserOpenID,
	}
	if activityUserNotificationExtInfoPtr != nil {
		extInfoBytes, marshalErr := json.Marshal(activityUserNotificationExtInfoPtr)
		if marshalErr != nil {
			err = marshalErr
			return
		}
		arg.ExtInfo = sqlc.NRawMessageFromRawMessage(json.RawMessage(extInfoBytes))
	}

	_, err = q.UpsertActivityUserNotification(ctx, arg)
	if err != nil {
		return
	}

	slog.Infof("新增/更新活动用户通知 ok")
	return
}
